<?php

	namespace org\tekuna\plugin\simplesecurity;
	
	use org\tekuna\base\Tekuna;
	
	use org\tekuna\core\configuration\ConfigurationException;
	use org\tekuna\core\configuration\ConfigurationElement;

	use org\tekuna\framework\interceptor\AbstractInterceptor;
	use org\tekuna\framework\interceptor\InvocationChain;
	use org\tekuna\framework\action\ActionEvent;
	use org\tekuna\framework\action\CliActionEvent;
	use org\tekuna\framework\response\http\UrlRedirectResponse;

	
	class SecurityInterceptor extends AbstractInterceptor {
		
		
		public function intercept(InvocationChain $objChain) {
			
			$objLogger = Tekuna :: getLogger(__CLASS__);
			$objActionEvent = $objChain -> getActionEvent();
			$objSecMgr = $this -> getApplicationContext() -> getSimpleSecurityManager();
			
			// perform security checks only if there are any relevant
			// security definitions for the current action
			if ($this -> roleRestrictionsRelevant($objActionEvent)) {
				
				// check for CLI requests
				if ($objActionEvent instanceof CliActionEvent) {
					
					throw new SecurityException("Security not implemented yet for CLI actions.");
				}
				
				// get the authenticated user
				$objUser = $objSecMgr -> getAuthenticatedUser();
				$sCurrentUrl = $objActionEvent -> getMatchedComponentActionUrl();
				
				// log a warning if no user found
				if ($objUser === null) {
					
					$objLogger -> warn("No authenticated user found for action '$sCurrentUrl'.");
				}
				
				// if there is no user or this user is not permitted
				if ($objUser === null || ! $objSecMgr -> isUserAuthorized($objUser, $objActionEvent)) {
					
					if ($objUser == null) {
						
						$objLogger -> warn("User not authorized for action '$sCurrentUrl'.");
					}
					else {
						
						$objLogger -> warn("User '$objUser->login' not authorized for action '$sCurrentUrl'.");
					}
					
					// special case to avoid infinite redirects: the unauthorized action itself is not authorized
					if ($objSecMgr -> getUnauthorizedAction() == $objActionEvent -> getMatchedComponentActionUrl()) {
						
						throw new SecurityException("Could not redirect to unauthorized page '{$objSecMgr->getUnauthorizedAction()}' because the page itself is not authorized. Are application-wide required roles defined?");
					}
					
					// send redirect to unauthorized action
					$objRedirectUrl = $objActionEvent -> buildActionUrl($objSecMgr -> getUnauthorizedAction());
					$objRedirectUrl -> setParameter('unauthorizedAction', $objActionEvent -> getMatchedComponentActionUrl());
					
					return new UrlRedirectResponse($objRedirectUrl);
				}
				else {
					
					$objLogger -> info("The user '$objUser->login' is authorized for the action '$sCurrentUrl'.");
				}
			}
						
			// continue the chain
			return $objChain -> invokeNext();
		}
		
		private function roleRestrictionsRelevant(ActionEvent $objActionEvent) {
			
			$bApplicationRoles = $this -> containsRoleDefinition($this -> getApplicationContext() -> getConfiguration() -> getRootElement());
			$bComponentRoles = $this -> containsRoleDefinition($objActionEvent -> getComponentElement());
			$bActionRoles = $this -> containsRoleDefinition($objActionEvent -> getActionElement());
			
			return $bApplicationRoles | $bComponentRoles | $bActionRoles;
		}
		
		private function containsRoleDefinition(ConfigurationElement $objElement) {
			
			if ($objElement -> hasAttribute('simple-security', 'requiredRoles')) {
				
				$sRoles = $objElement -> getAttribute('simple-security', 'requiredRoles') -> getValue();
				return trim($sRoles) != '';
			}
			
			return false;
		}
	}
